package upm.pb4agent.common;

import java.awt.Point;
import java.util.Date;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;

import upm.pb4agent.common.RegistrationDesk.RegistrationPeriodRemindTask;
import upm.pb4agent.ontology.AssignBoat;
import upm.pb4agent.ontology.Dock;
import upm.pb4agent.ontology.DockDesignation;
import upm.pb4agent.ontology.EnteringDock;
import upm.pb4agent.ontology.PB4AgentOntology;
import upm.pb4agent.ontology.RaceFinished;
import upm.pb4agent.ontology.RealRow;
import upm.pb4agent.ontology.Registration;
import upm.pb4agent.ontology.RegistrationForm;
import upm.pb4agent.ontology.Row;
import upm.pb4agent.ontology.Seat;
import upm.pb4agent.team8.Rower;
import upm.pb4agent.team8.Common.PB4PhysicsUtils;
import upm.pb4agent.team8.Common.PB4Utils;
import upm.pb4agent.team8.Common.PB4Properties;
import upm.pb4agent.team8.Common.RowerAttribute;
import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.Codec;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.SimpleBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

public class Watcher extends Agent
{
	private Codec codec = new SLCodec();
	private Ontology ontology = PB4AgentOntology.getInstance();
	
	protected int localDock = 0;
	private int _row_seconds = 0;
	private HashMap<String, Float> _rowersPreviousTiredness;
	private Point _currentPosition = new Point();
	private AID _teamBoatAID;
	private AID _refereeAID;
	
	protected void setup()
	{
		PB4Utils.Console.WriteLine( getLocalName() + ": Enters the system");
		
		getContentManager().registerLanguage(codec);
		getContentManager().registerOntology(ontology);

		PB4Utils.TryRegisterAgent(this, Constant.WATCHER);
		Registration registration= null;
		Object[] args = getArguments();
		if (args != null) {
			registration = (Registration) args[0];
		}
		_rowersPreviousTiredness = new HashMap<String, Float>();
		_teamBoatAID = PB4Utils.TryFindAgent(this, Constant.BOAT+ Constant.TEAM_ID);
		_refereeAID = PB4Utils.TryFindAgent(this, Constant.REFEREE);
		SetupBoat(registration);
		this.addBehaviour(new HandleRowingBehaviour(this));

	}
	
	class HandleRowingBehaviour extends CyclicBehaviour
	{
		Agent _agent;
		public HandleRowingBehaviour(Agent agent)
		{
			_agent = agent;
		}
		
		@Override
		public void action() 
		{
			ACLMessage msg = receive(MessageTemplate.MatchProtocol(PB4AgentOntology.ROW));	        
	        if (msg != null)
	        {
		        switch (msg.getPerformative()) 
		        {
		         	case ACLMessage.INFORM:
						ContentElement content = PB4Utils.GetMessageContent(_agent, msg);
						Concept action = ((Action)content).getAction();
						if (action instanceof Row )
						{
							Row row = (Row) action;
							Point resultP = CalculateRealRow( row, msg.getSender());
							NotifyRowingInformation(row,resultP);
							CalculateCurrentPosition(resultP);
						}
						break;
		        }
	        }
        }
		
		private Point CalculateRealRow( Row row, AID rowerID)
		{
			String rowName = rowerID.getLocalName();
			RowerAttribute attribute= PB4Properties.RowerAttributes.get(rowName);
			float impulse  = PB4PhysicsUtils.getImpulseValue( row.getStrength());
			
			float previousTiredness=0;

			try 
			{
				previousTiredness = _rowersPreviousTiredness.get(rowName);	
			} catch (Exception e) {}

			float tiredness = PB4PhysicsUtils.CalculateTiredness(previousTiredness,impulse, attribute.Resistance);
			_rowersPreviousTiredness.remove(rowName);
			_rowersPreviousTiredness.put(rowName, tiredness);
			
			//PB4Utils.Console.WriteLine("CalculateRealRow --"+rowName + " impulse:"+Float.toString(impulse)+ "tiredness:"+ Float.toString(tiredness) +" - Resistance:" + Float.toString(attribute.Resistance) + " previousTiredness: "+ Float.toString(previousTiredness));
			
			Point newPoint = PB4PhysicsUtils.CalculateRealRow(row, attribute.Strength, tiredness, impulse, row.getDirection());
			
			//PB4Utils.Console.WriteLine("CalculateRealRow" + newPoint.toString() + "OARID ="+ row.getOarID());
			
			return newPoint;
		}
	
		private void NotifyRowingInformation(Row row, Point p)
		{
			if (_teamBoatAID == null)
				_teamBoatAID = PB4Utils.TryFindAgent(_agent, Constant.BOAT+ Constant.TEAM_ID);
			
			RealRow realRow = new RealRow();
			realRow.setOarID(row.getOarID());
			realRow.setX(p.x);
			realRow.setY(p.y);
			
			//PB4Utils.Console.WriteLine("NotifyRowingInformation" +_teamBoatAID);
			PB4Utils.TrySendConceptMessage(ACLMessage.INFORM, _agent, _teamBoatAID, realRow);
		}
		 
		private void CalculateCurrentPosition(Point p)
		  {
			  _currentPosition.x +=p.x;
			  _currentPosition.y +=p.y;

		    int delta = (int)Math.ceil(Constant.CIRCUIT_LENGHT / 5);
		    
		    if (_currentPosition.y == (delta * 2))
		    	SendEnteringDockMessage("B");
		    else if (_currentPosition.y == (delta * 3))
		    	SendEnteringDockMessage("C");
		    else if (_currentPosition.y == (delta * 4))
		    	SendEnteringDockMessage("D");
		    else if (_currentPosition.y >= (delta * 5))
		    	SendRaceFinishMessage();

		  }
		  
		private void SendEnteringDockMessage( String nextDock)
		{
	    	EnteringDock enterDock=new EnteringDock();
	    	enterDock.setBoatAID(_teamBoatAID);
	    	Dock dock = new Dock();
	    	dock.setName(nextDock);
	    	
	    	enterDock.setDock(dock);
	    	enterDock.setPosition(1);
	    	enterDock.setTimeStamp((new Date().toString()));
	        PB4Utils.TrySendConceptMessage(ACLMessage.INFORM, _agent, _refereeAID, enterDock);
	        PB4Utils.Console.WriteLine("Boat " + Constant.TEAM_ID + " Entering Dock \"" +nextDock +"\"");
		}
		  
		private void SendRaceFinishMessage()
		{
			  RaceFinished finish = new RaceFinished();
			  finish.setBoatAID(_teamBoatAID);
			  finish.setPosition(1);
			  finish.setTimeStamp(new Date().toString());
			  PB4Utils.TrySendConceptMessage(ACLMessage.INFORM, _agent, _refereeAID, finish);
		}
	}

	private void SetupBoat(Registration registration)
	{
		String teamID = registration.getRegistrationForm().getTeamID().getValue();
		AID boatID = CreateTeamAgentBoat(teamID);
		if (boatID!=null)
		{
			NotifyRowersAssignedBoat(teamID, boatID, registration.getRegistrationForm());
		}
	}
	
	private AID CreateTeamAgentBoat(String teamID)
	{
		return PB4Utils.TryCreateAgent(this, "upm.pb4agent.common.Boat", Constant.BOAT + teamID, null);
	}
	
	private void NotifyRowersAssignedBoat(String teamID,  AID boatID, RegistrationForm form)
	{
		upm.pb4agent.ontology.Boat boat = new upm.pb4agent.ontology.Boat();
		boat.setSeats(CreateBoatSeats());
		boat.setID(boatID);
		
		for (int i = 0; i < form.getDockDesignation().size(); i++)
		{
			DockDesignation dock= (DockDesignation)form.getDockDesignation().get(i);
			AssignBoat assignedBoat =  new AssignBoat();
			assignedBoat.setBoat(boat);
			PB4Utils.TrySendConceptMessage(ACLMessage.INFORM, this, dock.getRowerID(), assignedBoat);
		}
	}		
	
	private jade.util.leap.List CreateBoatSeats()
	{
		jade.util.leap.List seats = new jade.util.leap.ArrayList();
		int maxBoatSeat = 6;
		for (int i = 1; i <= maxBoatSeat; i++) 
		{
			Seat s = new Seat();
			s.setSeatID(Integer.toString(i));
			seats.add(s);
		}
		return seats;
	}

}
